import sys
import os
import stat
import answer
import condition
import patientCondition
import orNode
import conditions
import time
import datetime
from string import Template

#Build the dictionary of synonyms to corresponding terms from a specific file
#File format is [term#]: [synonym1]; [synonym2]; [synonym3] ... ...
def buildHash(filePath):
    try:
        input = open(filePath,'r')
        s = input.readlines()
        dictionary = {}

        for line in s:
            term = line.split(':')[0].strip().lower()
            synonyms = line.split(':')[1].split(';')
            dictionary[term] = term
            for syn in synonyms:
                syn = syn.strip().lower()
                dictionary[syn] = term

        #print dictionary
        return dictionary                

    except IOError, (errno, strerror):
        print "I/O error(%s): %s" % (errno, strerror)
        
#For building hash dictionary for snomed findings
def buildFindingsHash(filePath):
    try:
        input = open(filePath,'r')
        s = input.readlines()
        dictionary = {}

        for line in s:
            colon_index = line.find(':')
            concept_id = line[0:colon_index].strip().lower()
            synonyms = line[colon_index+1:len(line)].split(';')
            for syn in synonyms:
                syn = syn.strip().lower().replace("[semi]", ";")
                dictionary[syn] = concept_id

        #print dictionary
        return dictionary                

    except IOError, (errno, strerror):
        print "I/O error(%s): %s" % (errno, strerror)
        
#Format output for an automatically generated file to 'output.txt'
#File format is [term]: 
def convertFile(filePath):
    try:
        input_file = open(filePath,'r')
        output_file = open('output.txt', 'w')
        s = input_file.readlines()

        for line in s:
            term = line.split('***')[0].strip().lower()
            term += ':\n'
            output_file.write(term)

    except IOError, (errno, strerror):
        print "I/O error(%s): %s" % (errno, strerror)

#Build the array of categoric terms from a specific file
def buildCategoricArray(filePath):
    try:
        input = open(filePath, 'r')
        s = input.readlines()
        categoricArray = []

        for line in s:
            term = line.strip().lower()
            categoricArray.append(term)

        return categoricArray
    
    except IOError, (errno, strerror):
        print "I/O error(%s): %s" % (errno, strerror)

#Build the array of categoric terms from a specific file
def buildArray(filePath):
    try:
        input = open(filePath, 'r')
        s = input.readlines()
        array = []

        for line in s:
            term = line.strip().lower()
            array.append(term)

        return array
    
    except IOError, (errno, strerror):
        print "I/O error(%s): %s" % (errno, strerror)
        
def getAnswers(answerString, ref_fns, stat_fns, var_dictionary):
    answers = [] #array of answers
    
    #split the answers seperated by the commas into an array namely 'answers'
    answer_parts = answerString.split(',')

    for answer_part in answer_parts:
        index = answer_part.find(' of ')
        ref_fn_array = [] #array of ref_fns
        stat_fn_array = [] #array of stat_fns
        if index != -1:
            fn_str = answer_part[0:index].strip() # function string before "OF"
            variable = answer_part[index + 4:len(answer_part)].strip()
            variable = getTerm(variable, var_dictionary)           

            if variable is not None:   

                fn_parts = fn_str.split(' and ') # split string using 'and'
                for fn_part in fn_parts:
                    ref_index = inList(ref_fns, fn_part)
                    if ref_index != -1:
                        ref_fn_array.append(fn_part)
                    stat_index = inList(stat_fns, fn_part)
                    if stat_index != -1:
                        stat_fn_array.append(fn_part)

                #create a new answer object
                ans = answer.answer(variable, ref_fn_array, stat_fn_array)

                #append answer object to array
                answers.append(ans)
        '''
        else:
            variable = answer_part.strip()
            variable = getTerm(variable, var_dictionary)
            
            #create a new answer object
            ans = answer.answer(variable)

            #append answer object to array
            answers.append(ans)
        '''

    return answers

# Use in separate SQL (single condition)
def getConditionArray(conditionString, comparands, conditionTypes, att_dictionary):
    current = 0 # current index
    conditionType = "and"
    conditions_array = []  # array of patient conditions

    # get the index of the first comparand
    index = getIndex(conditionString, comparands, current)
        
    while index != -1:
        # get the trimmed attribute (immediately before comparand)
        attribute = conditionString[current:index].strip()
        attribute = getTerm(attribute, att_dictionary)
        current = index

        # get the comparand (>, <, =, etc)
        comparand = str(getComparand(conditionString, index))
        compLen = len(comparand)

        # get the trimmed value (immediately after comparand)
        index = getIndex(conditionString, conditionTypes, current)
        if index == -1:
            value = conditionString[current + compLen:len(conditionString)].strip()
        else:
            value = conditionString[current + compLen:index].strip()

        # Create a new Condition object if attribute is defined
        if attribute is not None:
            cond = condition.condition(conditionType, attribute, comparand, value)
            
            # Append cond to array
            conditions_array.append(cond)

        conditionType = str(getConditionType(conditionString, index))
        typeLen = len(conditionType)

        current = index + typeLen

            # get the index of the next comparand
        if index != -1:
            index = getIndex(conditionString, comparands, current)
            
    return conditions_array

# Use in combined SQL (multiple conditions)
def getConditions(conditionString, comparands, conditionTypes, att_dictionary, isPatientCondition='F'):
    current = 0 # current index
    and_conditions = [] # array of and_conditions
    or_conditions = [] #array of or_conditions
    conditionType = "and"

    if conditionString == "" or conditionString is None:
        return None

    if isPatientCondition=='T':
        patient_conditions_array = []  # array of patient conditions

        # get the index of the first comparand
        index = getIndex(conditionString, comparands, current)
        
        while index != -1:
            # get the trimmed attribute (immediately before comparand)
            attribute = conditionString[current:index].strip()
            attribute = getTerm(attribute, att_dictionary)       

            current = index

            # get the comparand (>, <, =, etc)
            comparand = str(getComparand(conditionString, index))
            compLen = len(comparand)

            # get the trimmed value (immediately after comparand)
            index = getIndex(conditionString, conditionTypes, current)
            if index == -1:
                value = conditionString[current + compLen:len(conditionString)].strip()
            else:
                value = conditionString[current + compLen:index].strip()

            # Create a new Condition object if attribute is defined
            if attribute is not None:
                cond = patientCondition.patientCondition(conditionType, attribute, comparand, value)
            
                # Append cond to array
                patient_conditions_array.append(cond)

            conditionType = str(getConditionType(conditionString, index))
            typeLen = len(conditionType)

            current = index + typeLen

            # get the index of the next comparand
            if index != -1:
                index = getIndex(conditionString, comparands, current)
            
        return patient_conditions_array

    # get the index of the first comparand
    index = getIndex(conditionString, comparands, current)
    
    while index != -1:
        # get the trimmed attribute (immediately before comparand)
        attribute = conditionString[current:index].strip()
        attribute = getTerm(attribute, att_dictionary)
        current = index

        # get the comparand (>, <, =, etc)
        comparand = str(getComparand(conditionString, index))
        compLen = len(comparand)

        # get the trimmed value (immediately after comparand)
        index = getIndex(conditionString, conditionTypes, current)
        if index == -1:
            value = conditionString[current + compLen:len(conditionString)].strip()
        else:
            value = conditionString[current + compLen:index].strip()

        if index != -1:
            # get the condition type for the next condition, if any (AND, OR)
            conditionType = str(getConditionType(conditionString, index))
            if conditionType == 'and':
                
                # Create a new Condition object if attribute is defined
                if attribute is not None:
                    cond = condition.condition(conditionType, attribute, comparand, value)
                    
                    # Append cond to array
                    and_conditions.append(cond)
                    
                typeLen = len(conditionType)

                current = index + typeLen

                # get the index of the next comparand
                if index != -1:
                    index = getIndex(conditionString, comparands, current)
            
            elif conditionType == 'or':
                if attribute is not None:
                    cond = condition.condition(conditionType, attribute, comparand, value)
                    or_cond1 = cond #current condition is the first or_condition
                    
                    typeLen = len(conditionType)

                    current = index + typeLen

                    # get the index of the next comparand
                    if index != -1:
                        index = getIndex(conditionString, comparands, current)

                    # get the trimmed attribute (immediately before comparand)
                    attribute = conditionString[current:index].strip()
                    attribute = getTerm(attribute, att_dictionary)
                    current = index

                    # get the comparand (>, <, =, etc)
                    comparand = str(getComparand(conditionString, index))
                    compLen = len(comparand)

                    # get the trimmed value (immediately after comparand)
                    index = getIndex(conditionString, conditionTypes, current)
                    if index == -1:
                        value = conditionString[current + compLen:len(conditionString)].strip()
                    else:
                        value = conditionString[current + compLen:index].strip()

                    # Create a new Condition object

                    if attribute is not None:
                        cond = condition.condition(conditionType, attribute, comparand, value)
                        
                        or_cond2 = cond #current condition is the second or_condition

                        # Create a new orNode object
                        orNode_ = orNode.orNode(or_cond1, or_cond2)

                        # Append orNode to or_condition array
                        or_conditions.append(orNode_)

                    if index != -1:
                        # get the condition type for the next condition, if any (AND, OR)
                        conditionType = str(getConditionType(conditionString, index))
                        if conditionType == 'and':
                            typeLen = len(conditionType)

                            current = index + typeLen

                            # get the index of the next comparand
                            if index != -1:
                                index = getIndex(conditionString, comparands, current)
                            
                        elif conditionType == 'or':
                            return -1 # not allow consecutive or statements

        else:
            if attribute is not None:
                cond = condition.condition(conditionType, attribute, comparand, value)
                and_conditions.append(cond)

    conditions_ = conditions.conditions(and_conditions, or_conditions)
    return conditions_

# return the index of a string containing any one of the keys, starting at start
def getIndex(string, keys, start):
    indices = []
    for i in range(0,len(keys)):
        index = string.lower().find(keys[i].lower(), start)
        if index >= 0:
            indices.append(index)
    if len(indices) == 0:
        return -1
    else:
        indices.sort()
        return indices[0]

# return the key if 'string' contains it, giving the index of the string (got from getIndex())
def getKey(string, keys, index):
	for key in keys:
		if string[index:len(string)].lower().startswith(key.lower()):
			return key
	
	return -1
	
# return the comparand of a string given the index to '>' or '<'
def getComparand(string, index):
    if string[index] == '>':
    	if index == len(string) - 1:
    		return '>'
        elif string[index+1] == '=':
            return ">="
        else:
            return ">"
    elif string[index] == '<':
    	if index == len(string) - 1:
    		return '<'
        elif string[index+1] == '=':
            return "<="
        elif string[index+1] == '>':
            return "<>"
        else:
            return "<"
    elif string[index] == '=':
        return '='

# return the condition type given the index to 'AND' or 'OR'
def getConditionType(string, index):
    if string[index].lower() == 'a':
        return "and"
    elif string[index].lower() == 'o':
        return "or"

# return the corresponding database term for user input
def getTerm(string, dictionary):
    return dictionary.get(string.lower(), None)

# return the number of hours according to the time_entity
def getHours(time_entity, time_value):
    if time_entity == 'H':
        return int(time_value)
    elif time_entity == 'D':
        return int(time_value) * 24
    elif time_entity == 'W':
        return int(time_value) * 24 * 7
    elif time_entity == 'Y':
        return int(time_value) * 24 * 365
    else:
        return -1
    
# return the index of the keyword in a list
def inList(list_, key):
    index = 0
    for value in list_:
        if value == key:
	    return index
        else:
	    index = index + 1
    return -1

# gets current time
def getCurrentTime():
    return datetime.datetime.now()

# subTime subtracts current time 'now' by num_ent tim_ent (E.g. 48 "hours")
def subTime(now, num_ent, tim_ent):
    ##############changed 10/10#################
    #############default is hours###############
    interval = datetime.timedelta(hours = num_ent)
    if tim_ent == "days":
	interval = datetime.timedelta(days = num_ent)
    elif tim_ent == "weeks":
	interval = datetime.timedelta(weeks = num_ent)
    then = now - interval
    #print then.strftime("%Y %m %d %I")
    return then

# calculates the number of hours in num_ent time_ent (E.g. 2 days = 48 hours)
def toHours(num_ent, tim_ent):
    if tim_ent == "hours":
        return num_ent
    elif tim_ent == "days":
        return 24 * num_ent
    elif tim_ent == "weeks":
        return 7 * 24 * num_ent
    else:
        return -1

# obtains the time_entity (year, month, day, hour) of time
# E.g. time = 2007-1-15-09:24:30, getTimeEnt(time,"day") returns 15
def getTimeEnt(time, tim_ent):
    if tim_ent == "year":
        return time.strftime("%Y")
    elif tim_ent == "month":
        return time.strftime("%m")
    elif tim_ent == "day":
        return time.strftime("%d")
    elif tim_ent == "hour":
        return time.strftime("%H")
    elif tim_ent == "12hour":
        return time.strftime("%I")
    else:
        return -1

# converts the format used by real-time database
# E.g. 2007-07-27 20:00:00
def toRealTime(year, month, day, hour):
    if month < 10:
        mon = Template('0$mm').substitute(mm=month)
    if month > 9:
        mon = Template('$mm').substitute(mm=month)
    if day < 10:
        da = Template('0$dd').substitute(dd=day)
    if day > 9:
        da = Template('$dd').substitute(dd=day)
    if hour < 10:
        hr = Template('0$hh').substitute(hh=hour)
    if hour > 9:
        hr = Template('$hh').substitute(hh=hour)
    time = Template('$y-$m-$d $h:00:00').substitute(y=year, m=mon, d=da, h=hr)

    return time

# converts the format used by archive-time database
# E.g. 2007-07-27-20
def toArchiveTime(year, month, day, hour):
    if month < 10:
        mon = Template('0$mm').substitute(mm=month)
    if month > 9:
        mon = Template('$mm').substitute(mm=month)
    if day < 10:
        da = Template('0$dd').substitute(dd=day)
    if day > 9:
        da = Template('$dd').substitute(dd=day)
    if hour < 10:
        hr = Template('0$hh').substitute(hh=hour)
    if hour > 9:
        hr = Template('$hh').substitute(hh=hour)

    time = Template('$y-$m-$d-$h').substitute(y=year, m=mon, d=da, h=hr)

    return time

# combine two result sets into a single result set according to the logical operator
def combine(array1, array2, logical_operator):
    result_array = []
    if logical_operator.lower() == 'and':
        for value1 in array1:
            for value2 in array2:
                if value1 == value2:
                    result_array.append(value1)

    if logical_operator.lower() == 'or':
        for value1 in array1:
            result_array.append(value1)
            
        for value2 in array2:
            appeared = 0
            for value1 in array1:
                if value2 == value1:
                    appeared = 1

            if appeared == 0:
                result_array.append(value2)
            
    return result_array           

# return a list of final results from an array of results and corresponding logical operators, intersects with the correct patients' demographical info
def getFinalResult(patients, resultsArray, logicalArray):
    results = []

    if len(resultsArray) == 0:
        return patients
    elif len(resultsArray) == 1:
        results = resultsArray[0]
    else:
        results = combine(resultsArray[0], resultsArray[1], logicalArray[1])
        i = 2
        while i != len(resultsArray):
            results = combine(results, resultsArray[i], logicalArray[i])
            i = i + 1

    if patients is None:
        return results
    
    return combine(patients, results, 'and')

# return a combination of the realtime and archival database
def joinResults(array1, array2):
    result_array = array1
    if array2 is not None:
	for value2 in array2:
	    result_array.append(value2)
        
    return result_array
    
# transform the patient condition according to the corresponding database
# E.g. for the cond "sex = male", it transforms to "sex = 'M'" for archival db, or "sex = 0" for realtime db
def transform_Conditions(conditions, db):
    if conditions is None:
        return None

    conditionsArray = []
    if db == "archive":
        for condition in conditions:
            att = condition.getAtt()
            val = condition.getVal()
            if att == "sex" and val == "male":
                condition.setVal('M')
            elif att == "sex" and val == "female":
                condition.setVal('F')
            conditionsArray.append(condition)
    elif db == "realtime":
        for condition in conditions:
            att = condition.getAtt()
            val = condition.getVal()
            if att == "sex" and val == "male":
                condition.setVal(0)
            elif att == "sex" and val == "female":
                condition.setVal(1)
            conditionsArray.append(condition)

    return conditionsArray

# Add the list of terms in query
# E.g. if group = 'sedation', then it returns ('Propofol','Fentanyl',...) to the query
def addTerms(group):
    line = "("

    if group == "sedation":
        path = 'doc/group_sedation_terms.txt'
    elif group == "inotrope":
        path = 'doc/group_inotropes_terms.txt'
    elif group == "antibiotic":
        path = 'doc/group_antibiotic_terms.txt'
    elif group == "thromboembolic prophylaxis":
        path = 'doc/group_thromboembolic_terms.txt'
        
    terms = buildCategoricArray(path)
    line = line + "'" + terms[0] + "'"
    for i in range(1,len(terms)):
        line = line + ",'" + terms[i] + "'"

    line = line + ")"
    return line

# Remove Null values in an array
def removeNull(values):
    new_values = []
    for n in range(0, len(values)):
        try:
            float(str(values[n]))
            new_values.append(values[n])
        except ValueError:
            continue
    
    return new_values

# values in the form [mrn] [value] [chart-time]
# return all numeric value
# use by statistics such as mean and sd
def getValues(values, col = 1):
    new_values = []
    
    for n in range(0, len(values)):
        res = str(values[n]).split(';')
        try:
            res[col] = float(str(res[col]))
            new_values.append(res[col])
        except ValueError:
            continue
        except IndexError:
            continue
    
    return new_values

#Get the hypothesis testing result based on the keyword (such as t and pval)
def getHypTestResult(keyword):
    try:
        fid = open('test_results.txt','r')
        s = fid.readlines()

        for line in s:
            term = line.split(':')[0].strip()
            if term == keyword:
                return line.split(':')[1].strip()
        return -1

    except IOError, (errno, strerror):
        print "I/O error(%s): %s" % (errno, strerror)
        
